// Copyright (c) 2010 Eugene Korneychuk (zhech)
// This source is subject to the  MIT License.
// http://zhechajaxcontrols.codeplex.com/license.
// All other rights reserved.

AjaxControlToolkit._CommonToolkitScripts.prototype.getClientBounds = function () {
        /// <summary>
        /// Gets the width and height of the browser client window (excluding scrollbars)
        /// </summary>
        /// <returns type="Sys.UI.Bounds">
        /// Browser's client width and height
        /// </returns>

        var clientWidth;
        var clientHeight;

        // getClientBounds must return dimensions excluding scrollbars, so cannot use window.innerWidth and window.innerHeight.
        if (document.compatMode == "CSS1Compat") {
            // Standards-compliant mode
            clientWidth = document.documentElement.clientWidth;
            clientHeight = document.documentElement.clientHeight;
        } else {
            // Quirks mode
            clientWidth = document.body.clientWidth;
            clientHeight = document.body.clientHeight;
        }
        var scrOfX = 0,
                scrOfY = 0;
        if (typeof (window.pageYOffset) == 'number') {
            scrOfY = window.pageYOffset;
            scrOfX = window.pageXOffset;
        } else if (document.body && (document.body.scrollLeft || document.body.scrollTop)) {
            scrOfY = document.body.scrollTop;
            scrOfX = document.body.scrollLeft;
        } else if (document.documentElement && (document.documentElement.scrollLeft || document.documentElement.scrollTop)) {
            scrOfY = document.documentElement.scrollTop;
            scrOfX = document.documentElement.scrollLeft;
        }
        return new Sys.UI.Bounds(scrOfX, scrOfY, clientWidth, clientHeight);
};


// Temporary fix null reference bug in Sys.CultureInfo._getAbbrMonthIndex
if (Sys.CultureInfo.prototype._getAbbrMonthIndex) {
    try {
        Sys.CultureInfo.prototype._getAbbrMonthIndex('');
    } catch (ex) {
        Sys.CultureInfo.prototype._getAbbrMonthIndex = function (value) {
            if (!this._upperAbbrMonths) {
                this._upperAbbrMonths = this._toUpperArray(this.dateTimeFormat.AbbreviatedMonthNames);
            }
            return Array.indexOf(this._upperAbbrMonths, this._toUpper(value));
        }
        Sys.CultureInfo.CurrentCulture._getAbbrMonthIndex = Sys.CultureInfo.prototype._getAbbrMonthIndex;
        Sys.CultureInfo.InvariantCulture._getAbbrMonthIndex = Sys.CultureInfo.prototype._getAbbrMonthIndex;
    }
}
if (Sys.Browser.agent === Sys.Browser.WebKit) {
    Sys.UI.DomElement.getLocation = function (element) {
        /// <summary>Gets the coordinates of a DOM element.</summary>
        /// <param name="element" domElement="true"/>
        /// <returns type="Sys.UI.Point">
        ///   A Point object with two fields, x and y, which contain the pixel coordinates of the element.
        /// </returns>
        // For a document element, return zero.
        if ((element.window && (element.window === element)) || element.nodeType === 9) return new Sys.UI.Point(0, 0);

        var offsetX = 0,
                offsetY = 0,
                parent, previous = null,
                previousStyle = null,
                currentStyle;
        for (parent = element; parent; previous = parent, previousStyle = currentStyle, parent = parent.offsetParent) {
            currentStyle = Sys.UI.DomElement._getCurrentStyle(parent);
            var tagName = parent.tagName ? parent.tagName.toUpperCase() : null;

            if ((parent.offsetLeft || parent.offsetTop) && ((tagName !== "BODY") || (!previousStyle || previousStyle.position !== "absolute"))) {
                offsetX += parent.offsetLeft;
                offsetY += parent.offsetTop;
            }

            if (previous && Sys.Browser.version >= 3) {
                offsetX += parseInt(currentStyle.borderLeftWidth);
                offsetY += parseInt(currentStyle.borderTopWidth);
            }
        }

        currentStyle = Sys.UI.DomElement._getCurrentStyle(element);
        var elementPosition = currentStyle ? currentStyle.position : null;
        // If an element is absolutely positioned, its parent's scroll should not be subtracted
        if (!elementPosition || (elementPosition !== "absolute")) {
            for (parent = element.parentNode; parent; parent = parent.parentNode) {
                tagName = parent.tagName ? parent.tagName.toUpperCase() : null;

                if ((tagName !== "BODY") && (tagName !== "HTML") && (parent.scrollLeft || parent.scrollTop)) {
                    offsetX -= (parent.scrollLeft || 0);
                    offsetY -= (parent.scrollTop || 0);
                }
                currentStyle = Sys.UI.DomElement._getCurrentStyle(parent);
                var parentPosition = currentStyle ? currentStyle.position : null;

                if (parentPosition && (parentPosition === "absolute")) break;
            }
        }
        return new Sys.UI.Point(offsetX, offsetY);
    }
}
// Create scrollWidth method
jQuery.fn['scrollWidth'] = function (val) {
    if (!this[0]) {
        return 0;
    }
    var result = 0;
    if (val !== undefined) {
        return this.each(function () {
            this == window || this == document ? window.scrollWidth(val) : this['scrollWidth'] = val;
        });
    } else {
        if (this[0] == window || this[0] == document) {
            return self['scrollWidth'] || $.boxModel && document.documentElement['scrollWidth'] || document.body['scrollWidth'];
        } else {
            return this[0]['scrollWidth'];
        }
    }
};
jQuery.fn['scrollHeight'] = function (val) {
    if (!this[0]) return 0;
    if (val !== undefined) {
        return this.each(function () {
            this == window || this == document ? window.scrollHeihgt(val) : this['scrollHeight'] = val;
        });
    } else {
        if (this[0] == window || this[0] == document) {
            return self['scrollHeight'] || $.boxModel && document.documentElement['scrollHeight'] || document.body['scrollHeight'];
        } else {
            return this[0]['scrollHeight'];
        }
    }
};

jQuery.fn.extend({
    dropdownBy: function (args) {
        args = $.extend({
            target: null,
            // A raw x,y coordinate
            offsetX: 0,
            offsetY: 0,
            positionMode: AjaxControlToolkit.PositioningMode.BottomLeft,
            changeWidth: true,
            changeHeight: true
        },
            args);
        var $target = $($(args.target)[0]);
        var tOffset = $target.offset();
        var boundsBC = {
            x: tOffset.left,
            y: tOffset.top,
            width: $target.innerWidth(),
            height: $target.outerHeight(),
            divX: $target.outerWidth() - $target.innerWidth()
        };
        boundsBC.right = boundsBC.x + boundsBC.width;
        boundsBC.bottom = boundsBC.y + boundsBC.height;
        var viewportBounds = $common.getClientBounds();
        return this.each(function () {
            try {
                var $element = $(this);
                var $children = $element.children();
                if ($children.size() == 0) {
                    return;
                }
                $element.css({
                    left: -3000,
                    top: -3000
                });
                if (args.changeWidth) {
                    $element.width(1);
                    $children.each(function () {
                        $(this).width(1);
                    });
                }
                $element.show();
                var elemBounds = {
                    x: boundsBC.x + args.offsetX,
                    y: boundsBC.bottom + args.offsetY,
                    width: $element.width() || 0,
                    height: $element.height() || 0,
                    divX: ($element.outerWidth(true) - $element.width()) || 0,
                    divY: ($element.outerHeight(true) - $element.height()) || 0
                };
                var itemDiv = 0;
                if (args.changeWidth) {
                    $children.first().each(function () {
                        itemDiv = ($(this).outerWidth(true) - $(this).width()) || 0;
                    });
                    if (elemBounds.width < boundsBC.width - args.offsetX) {
                        elemBounds.width = boundsBC.width - args.offsetX - boundsBC.divX;
                    }
                    if (elemBounds.width < 60) {
                        elemBounds.width += 16;
                    }
                    if ((elemBounds.width > boundsBC.width) && (boundsBC.width > 300)) {
                        elemBounds.width = boundsBC.width;
                    }
                    $element.width(elemBounds.width);
                    $children.each(function () {
                        $(this).width(elemBounds.width - itemDiv);
                    });
                }
                if (args.changeHeight === true) {
                    elemBounds.height = 0;
                }
                if (args.changeHeight || (elemBounds.height == 0)) {
                    $children.each(function () {
                        if ($(this).is(':visible'))
                            elemBounds.height += ($(this).outerHeight(true) || 0);
                    });
                }
                switch (args.positionMode) {
                    case AjaxControlToolkit.PositioningMode.Center:
                        elemBounds.x += Math.round(boundsBC.width / 2);
                        elemBounds.y -= Math.round(boundsBC.height / 2);
                        break;
                    case AjaxControlToolkit.PositioningMode.BottomRight:
                        elemBounds.x += boundsBC.width;
                        break;
                    case AjaxControlToolkit.PositioningMode.TopLeft:
                        elemBounds.y -= boundsBC.height - elemBounds.height;
                        break;
                    case AjaxControlToolkit.PositioningMode.TopRight:
                        elemBounds.x += boundsBC.width;
                        elemBounds.y -= boundsBC.height - elemBounds.height;
                        break;
                    case AjaxControlToolkit.PositioningMode.Right:
                        elemBounds.x += boundsBC.width;
                        break;
                    case AjaxControlToolkit.PositioningMode.Left:
                        break;
                }

                var topHeight = boundsBC.y - viewportBounds.y - args.offsetY;
                var bottomHeight = viewportBounds.y + viewportBounds.height - boundsBC.y - boundsBC.height - args.offsetY;
                var isBottomPanel = true;
                var setScrollbar = false;
                if ((bottomHeight >= topHeight) && (bottomHeight < elemBounds.height)) {
                    if (args.changeHeight) {
                        elemBounds.height = bottomHeight - 4;
                        setScrollbar = true;
                    }
                } else if ((bottomHeight < topHeight) && (topHeight > elemBounds.height)) {
                    if (args.changeHeight) {
                        elemBounds.height = ((topHeight - viewportBounds.y - 1) || 0);
                        setScrollbar = true;
                        elemBounds.y = boundsBC.y - topHeight + viewportBounds.y + 1;
                    } else {
                        elemBounds.y = boundsBC.y - elemBounds.height - elemBounds.divY + viewportBounds.y + 2;
                    }
                }
                if (setScrollbar && args.changeWidth) {
                    $element.width(elemBounds.width + 16);
                }
                if (viewportBounds.x + viewportBounds.width < elemBounds.x + elemBounds.width) {
                    elemBounds.x = boundsBC.x + boundsBC.width - elemBounds.width - args.offsetX;
                }
                try {
                    $element.offset({
                        left: elemBounds.x,
                        top: elemBounds.y
                    });
                    if (args.changeHeight) {
                        $element.height(elemBounds.height);
                    }
                } catch (e) {
                    $common.setBounds(this, elemBounds);
                }
            }
            catch (exc) {
                Err(exc);
            }
        });
    }
});
function Trim(value) {
    if (value) {
        return value.replace("/^\s+|\s+$/g|\'", '');
    }
    return value;
}
function lp(obj, separator) {
    if (!separator) {
        separator = "<br/>";
    }
    var s = '';
    for (var x in obj) {
        s += x + ":";
        try {
            var o = obj[x];
            s += (typeof o == "function") ? "[function]" : o;
        } catch (e) {
        }
        s += separator;
    }
    return s;
};
function Log(msg, separator, output) {
    var time = new Date();
    var timeStamp = time.getHours() + ":" + time.getMinutes() + ":" + time.getSeconds() + "_" + time.getMilliseconds();
    if (!separator) {
        separator = "<br/>";
    }
    if (output) {
        $(output).prepend("<span>" + timeStamp + ": " + msg + "</span>" + separator);
    } else {
        alert(msg);
    }
};
function Err(exc, separator, output) {
    if (!separator) {
        separator = "<br/>";
    }
    var n = "";
    if (exc) {
        n += "Message: " + exc.message + ";" + separator;
        n += "Name: " + exc.name + ";" + separator;
        n += "Arguments: " + exc.arguments + ";" + separator;
    }
    n += "StackTrace:" + separator + printStackTrace({e: exc, guess: true }) + ";" + separator;
    Log("Exception" + separator + n, separator, output);
};

function printStackTrace(options) {
    var ex = (options && options.e) ? options.e : null;
    var guess = (options && options.guess) ? options.guess : false;

    var p = new printStackTrace.implementation();
    var result = p.run(ex);
    return (guess) ? p.guessFunctions(result) : result;
}

printStackTrace.implementation = function () { };

printStackTrace.implementation.prototype = {
    run: function (ex) {
        // Use either the stored mode, or resolve it
        var mode = this._mode || this.mode();
        if (mode === 'other') {
            return this.other(arguments.callee);
        }
        else {
            ex = ex ||
                (function () {
                    try {
                        (0)();
                    } catch (e) {
                        return e;
                    }
                })();
            return this[mode](ex);
        }
    },

    mode: function () {
        try {
            (0)();
        } catch (e) {
            if (e.arguments) {
                return (this._mode = 'chrome');
            }
            if (e.stack) {
                return (this._mode = 'firefox');
            }
            if (window.opera && !('stacktrace' in e)) { //Opera 9-
                return (this._mode = 'opera');
            }
        }
        return (this._mode = 'other');
    },

    chrome: function (e) {
        return e.stack.replace(/^.*?\n/, '').
                replace(/^.*?\n/, '').
                replace(/^.*?\n/, '').
                replace(/^[^\(]+?[\n$]/gm, '').
                replace(/^\s+at\s+/gm, '').
                replace(/^Object.<anonymous>\s*\(/gm, '{anonymous}()@').
                split("\n");
    },

    firefox: function (e) {
        return e.stack.replace(/^.*?\n/, '').
                replace(/(?:\n@:0)?\s+$/m, '').
                replace(/^\(/gm, '{anonymous}(').
                split("\n");
    },

    // Opera 7.x and 8.x only!
    opera: function (e) {
        var lines = e.message.split("\n"), ANON = '{anonymous}',
            lineRE = /Line\s+(\d+).*?script\s+(http\S+)(?:.*?in\s+function\s+(\S+))?/i, i, j, len;

        for (i = 4, j = 0, len = lines.length; i < len; i += 2) {
            if (lineRE.test(lines[i])) {
                lines[j++] = (RegExp.$3 ? RegExp.$3 + '()@' + RegExp.$2 + RegExp.$1 : ANON + '()@' + RegExp.$2 + ':' + RegExp.$1) +
                ' -- ' +
                lines[i + 1].replace(/^\s+/, '');
            }
        }

        lines.splice(j, lines.length - j);
        return lines;
    },

    // Safari, Opera 9+, IE, and others
    other: function (curr) {
        var ANON = "{anonymous}", fnRE = /function\s*([\w\-$]+)?\s*\(/i, stack = [], j = 0, fn, args;

        var maxStackSize = 10;
        while (curr && stack.length < maxStackSize) {
            fn = fnRE.test(curr.toString()) ? RegExp.$1 || ANON : ANON;
            args = Array.prototype.slice.call(curr['arguments']);
            stack[j++] = fn + '(' + printStackTrace.implementation.prototype.stringifyArguments(args) + ')';

            //Opera bug: if curr.caller does not exist, Opera returns curr (WTF)
            if (curr === curr.caller && window.opera) {
                //TODO: check for same arguments if possible
                break;
            }
            curr = curr.caller;
        }
        return stack;
    },

    stringifyArguments: function (args) {
        for (var i = 0; i < args.length; ++i) {
            var argument = args[i];
            if (typeof argument == 'object') {
                args[i] = '#object';
            } else if (typeof argument == 'function') {
                args[i] = '#function';
            } else if (typeof argument == 'string') {
                args[i] = '"' + argument + '"';
            }
        }
        return args.join(',');
    },

    sourceCache: {},

    ajax: function (url) {
        var req = this.createXMLHTTPObject();
        if (!req) {
            return;
        }
        req.open('GET', url, false);
        req.setRequestHeader("User-Agent", "XMLHTTP/1.0");
        req.send('');
        return req.responseText;
    },

    createXMLHTTPObject: function () {
        // Try XHR methods in order and store XHR factory
        var xmlhttp, XMLHttpFactories = [
            function () {
                return new XMLHttpRequest();
            }, function () {
                return new ActiveXObject("Msxml2.XMLHTTP");
            }, function () {
                return new ActiveXObject("Msxml3.XMLHTTP");
            }, function () {
                return new ActiveXObject("Microsoft.XMLHTTP");
            }
        ];
        for (var i = 0; i < XMLHttpFactories.length; i++) {
            try {
                xmlhttp = XMLHttpFactories[i]();
                // Use memoization to cache the factory
                this.createXMLHTTPObject = XMLHttpFactories[i];
                return xmlhttp;
            } catch (e) { }
        }
    },

    getSource: function (url) {
        if (!(url in this.sourceCache)) {
            this.sourceCache[url] = this.ajax(url).split("\n");
        }
        return this.sourceCache[url];
    },

    guessFunctions: function (stack) {
        for (var i = 0; i < stack.length; ++i) {
            var reStack = /{anonymous}\(.*\)@(\w+:\/\/([-\w\.]+)+(:\d+)?[^:]+):(\d+):?(\d+)?/;
            var frame = stack[i], m = reStack.exec(frame);
            if (m) {
                var file = m[1], lineno = m[4]; //m[7] is character position in Chrome
                if (file && lineno) {
                    var functionName = this.guessFunctionName(file, lineno);
                    stack[i] = frame.replace('{anonymous}', functionName);
                }
            }
        }
        return stack;
    },

    guessFunctionName: function (url, lineNo) {
        try {
            return this.guessFunctionNameFromLines(lineNo, this.getSource(url));
        } catch (e) {
            return 'getSource failed with url: ' + url + ', exception: ' + e.toString();
        }
    },

    guessFunctionNameFromLines: function (lineNo, source) {
        var reFunctionArgNames = /function ([^(]*)\(([^)]*)\)/;
        var reGuessFunction = /['"]?([0-9A-Za-z_]+)['"]?\s*[:=]\s*(function|eval|new Function)/;
        // Walk backwards from the first line in the function until we find the line which
        // matches the pattern above, which is the function definition
        var line = "", maxLines = 10;
        for (var i = 0; i < maxLines; ++i) {
            line = source[lineNo - i] + line;
            if (line !== undefined) {
                var m = reGuessFunction.exec(line);
                if (m) {
                    return m[1];
                }
                else {
                    m = reFunctionArgNames.exec(line);
                }
                if (m && m[1]) {
                    return m[1];
                }
            }
        }
        return "(?)";
    }
};